package gupao.concurrency.threads;

import lombok.RequiredArgsConstructor;

import java.sql.Ref;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

public class AtomicReferenceSample2 {

    public static void main(String[] args){
//        AtomicReferenceSample sample = new AtomicReferenceSample();
        NonAtomicRefSample sample = new NonAtomicRefSample();
        List<Thread> tList = IntStream.range(0,10).mapToObj(i -> new Thread(() -> {
            Utils.waitNanos(1000 * 5);
            sample.increaseAccount();
        })).collect(Collectors.toList());
        tList.forEach(Thread::start);
        tList.forEach( t -> {
            try {
                t.join();
            } catch (InterruptedException e) {
            }
        });
        System.out.println("Current account num: " + sample.getNum());
    }

    @RequiredArgsConstructor
    static class BasicAccount{
        final int num;
    }

    static class NonAtomicRefSample implements ReferenceSample{
        volatile BasicAccount basicAccountRef = new BasicAccount(0);
        void increaseAccount(){
            final BasicAccount currentAccount = basicAccountRef;
            BasicAccount newAccount = new BasicAccount(currentAccount.num+1);
            basicAccountRef = newAccount;
        }

        @Override
        public int getNum() {
            return basicAccountRef.num;
        }
    }

    static class AtomicReferenceSample implements ReferenceSample {
        AtomicReference<BasicAccount> basicAccountRef = new AtomicReference<>(new BasicAccount(0));

        void increaseAccount(){
            BasicAccount currentAccount = null, nextAccount = null;
            do{
                currentAccount = basicAccountRef.get();
                nextAccount = currentAccount == null ? new BasicAccount(0)
                        : new BasicAccount(currentAccount.num+1);
            }while (!basicAccountRef.compareAndSet(currentAccount,nextAccount));
        }

        @Override
        public int getNum() {
            return basicAccountRef.get().num;
        }
    }

    static interface ReferenceSample{
        int getNum();
    }

}
